﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Windows;
using System.Windows.Controls;

namespace DaveSexton.Labs
{
	public partial class PhoneLabHost : UserControl, ILabHost
	{
		#region Public Properties
		public static readonly DependencyProperty HasSourceCodeProperty = DependencyProperty.Register("HasSourceCode", typeof(bool), typeof(PhoneLabHost), new PropertyMetadata(false));
		public static readonly DependencyProperty SourceCodeProperty = DependencyProperty.Register("SourceCode", typeof(string), typeof(PhoneLabHost), new PropertyMetadata(null));
		public static readonly DependencyProperty HasSourceXamlProperty = DependencyProperty.Register("HasSourceXaml", typeof(bool), typeof(PhoneLabHost), new PropertyMetadata(false));
		public static readonly DependencyProperty SourceXamlProperty = DependencyProperty.Register("SourceXaml", typeof(string), typeof(PhoneLabHost), new PropertyMetadata(null));
		public static readonly DependencyProperty CanChangeLabProperty = DependencyProperty.Register("CanChangeLab", typeof(bool), typeof(PhoneLabHost), new PropertyMetadata(true));
		public static readonly DependencyProperty HasSelectedItemProperty = DependencyProperty.Register("HasSelectedItem", typeof(bool), typeof(PhoneLabHost), new PropertyMetadata(false));
		public static readonly DependencyProperty LabsProperty = DependencyProperty.Register("Labs", typeof(IList<LabGroup>), typeof(PhoneLabHost), new PropertyMetadata(null));
		public static readonly DependencyProperty SelectedLabProperty = DependencyProperty.Register("SelectedLab", typeof(ILab), typeof(PhoneLabHost), new PropertyMetadata(null));

		public ILab SelectedLab
		{
			get
			{
				return (ILab)GetValue(SelectedLabProperty);
			}
			set
			{
				SetValue(SelectedLabProperty, value);
			}
		}

		public string ApplicationTitle
		{
			get
			{
				return (PivotControl.Title ?? string.Empty).ToString();
			}
			set
			{
				PivotControl.Title = value;
			}
		}

		public bool HasSourceCode
		{
			get
			{
				object value = GetValue(HasSourceCodeProperty);

				Contract.Assume(value is bool);

				return (bool)value;
			}
			set
			{
				SetValue(HasSourceCodeProperty, value);
			}
		}

		public string SourceCode
		{
			get
			{
				return (string)GetValue(SourceCodeProperty);
			}
			set
			{
				SetValue(SourceCodeProperty, value);
			}
		}

		public bool HasSourceXaml
		{
			get
			{
				object value = GetValue(HasSourceXamlProperty);

				Contract.Assume(value is bool);

				return (bool)value;
			}
			set
			{
				SetValue(HasSourceXamlProperty, value);
			}
		}

		public string SourceXaml
		{
			get
			{
				return (string)GetValue(SourceXamlProperty);
			}
			set
			{
				SetValue(SourceXamlProperty, value);
			}
		}

		public bool CanChangeLab
		{
			get
			{
				object value = GetValue(CanChangeLabProperty);

				Contract.Assume(value is bool);

				return (bool)value;
			}
			set
			{
				SetValue(CanChangeLabProperty, value);
			}
		}

		public bool HasSelectedItem
		{
			get
			{
				object value = GetValue(HasSelectedItemProperty);

				Contract.Assume(value is bool);

				return (bool)value;
			}
			set
			{
				SetValue(HasSelectedItemProperty, value);
			}
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly",
			Justification = "Dependency property.")]
		public IList<LabGroup> Labs
		{
			get
			{
				return (IList<LabGroup>)GetValue(LabsProperty);
			}
			set
			{
				SetValue(LabsProperty, value);
			}
		}
		#endregion

		#region Private / Protected
		private bool hasSelectedLab;
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="PhoneLabHost" /> class.
		/// </summary>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors",
			Justification = "ContractInvariantMethod.")]
		public PhoneLabHost()
		{
			InitializeComponent();

			LabList.SelectionChanged += LabList_SelectionChanged;
		}
		#endregion

		#region Methods
		[ContractInvariantMethod]
		private void ObjectInvariant()
		{
			Contract.Invariant(LabList != null);
			Contract.Invariant(Output != null);
			Contract.Invariant(PivotControl != null);
		}

		public void LoadLabs(LabCategory category)
		{
			Func<Lab, Lab> initialize = lab =>
			{
				Contract.Assume(lab != null);

				var labToAdd = lab.IsProxy ? lab : PhoneConsoleLabHost.Create(lab);

				Contract.Assume(labToAdd.IsProxy);

				var actual = labToAdd.ActualLab as PhoneLab;

				if (actual != null)
				{
					actual.Host = this;
				}

				return labToAdd;
			};

			this.Labs = (from lab in category.LabsRecursive
									 orderby lab.DisplayName
									 group initialize(lab) by lab.Category into labs
									 orderby labs.Key.OrganizedName
									 select new LabGroup(labs.Key, labs.ToList().AsReadOnly()))
									 .ToList()
									 .AsReadOnly();
		}

		public void SetCurrentLab(Lab lab)
		{
			throw new NotSupportedException();
		}

		public void Write(string message, TraceEventType eventType)
		{
			Dispatcher.BeginInvoke(() =>
			{
				Output.Text += message;

				OutputScroll.UpdateLayout();
				OutputScroll.ScrollToVerticalOffset(OutputScroll.ExtentHeight);
			});
		}

		public void WriteLine(string message, TraceEventType eventType)
		{
			Dispatcher.BeginInvoke(() =>
			{
				Output.Text += message + Environment.NewLine;

				OutputScroll.UpdateLayout();
				OutputScroll.ScrollToVerticalOffset(OutputScroll.ExtentHeight);
			});
		}

		public void Clear()
		{
			Dispatcher.BeginInvoke(() => Output.Text = string.Empty);
		}
		#endregion

		#region Event Handlers
		private void LabList_SelectionChanged(object sender, SelectionChangedEventArgs e)
		{
			var lab = (Lab)LabList.SelectedItem;

			SelectedLab = lab == null ? null : lab.ActualLab;
			HasSelectedItem = lab != null;

			bool hasSourceCode = lab != null && !string.IsNullOrEmpty(lab.SourceCode);

			HasSourceCode = hasSourceCode;
			SourceCode = hasSourceCode ? lab.SourceCode : null;

			bool hasSourceXaml = lab != null && !string.IsNullOrEmpty(lab.SourceXaml);

			HasSourceXaml = hasSourceXaml;
			SourceXaml = hasSourceXaml ? lab.SourceXaml : null;

			if (hasSelectedLab)
			{
				Clear();
			}
			else
			{
				hasSelectedLab = true;
			}

			if (lab != null)
			{
				PivotControl.SelectedItem = ConsolePivotItem;
			}
		}
		#endregion
	}
}